Introduction
# load in the libraries
library(stringi)
library(plotly)
Loading required package: ggplot2
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 methods overwritten by 'htmltools':
method from
print.html tools:rstudio
print.shiny.tag tools:rstudio
print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
library(plyr)
Attaching package: ‘plyr’
The following objects are masked from ‘package:plotly’:
arrange, mutate, rename, summarise
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
[30m── [1mAttaching packages[22m ──────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──[39m
[30m[32m✓[30m [34mtibble [30m 2.1.3 [32m✓[30m [34mdplyr [30m 0.8.3
[32m✓[30m [34mtidyr [30m 1.0.0 [32m✓[30m [34mstringr[30m 1.4.0
[32m✓[30m [34mreadr [30m 1.3.1 [32m✓[30m [34mforcats[30m 0.4.0
[32m✓[30m [34mpurrr [30m 0.3.3 [39m
[30m── [1mConflicts[22m ─────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
[31mx[30m [34mdplyr[30m::[32marrange()[30m masks [34mplyr[30m::arrange(), [34mplotly[30m::arrange()
[31mx[30m [34mpurrr[30m::[32mcompact()[30m masks [34mplyr[30m::compact()
[31mx[30m [34mdplyr[30m::[32mcount()[30m masks [34mplyr[30m::count()
[31mx[30m [34mdplyr[30m::[32mfailwith()[30m masks [34mplyr[30m::failwith()
[31mx[30m [34mdplyr[30m::[32mfilter()[30m masks [34mplotly[30m::filter(), [34mstats[30m::filter()
[31mx[30m [34mdplyr[30m::[32mid()[30m masks [34mplyr[30m::id()
[31mx[30m [34mdplyr[30m::[32mlag()[30m masks [34mstats[30m::lag()
[31mx[30m [34mdplyr[30m::[32mmutate()[30m masks [34mplyr[30m::mutate(), [34mplotly[30m::mutate()
[31mx[30m [34mdplyr[30m::[32mrename()[30m masks [34mplyr[30m::rename(), [34mplotly[30m::rename()
[31mx[30m [34mdplyr[30m::[32msummarise()[30m masks [34mplyr[30m::summarise(), [34mplotly[30m::summarise()
[31mx[30m [34mdplyr[30m::[32msummarize()[30m masks [34mplyr[30m::summarize()[39m
Analysis
Read in the data
Read in data from files
beer = read.csv("./data/Beers.csv",header = TRUE)
breweries = read.csv("./data/Breweries.csv",header = TRUE, strip.white = TRUE)
#display the dataframes
beer
breweries
NA
How many breweries are there in each state?
Heat Map of Breweies per State
## add lowercase state name for heat map
lstates = tolower(state.name)
state_count = state_breweies %>%
add_rownames("region") %>%
mutate( region=lstates[match(State, state.abb)] )
Deprecated, use tibble::rownames_to_column() instead.
## find center of each state for text position
snames <- data.frame(region=lstates, long=state.center$x, lat=state.center$y)
snames <- merge(snames, state_count, by="region")
## merge map data with counts data
choro <- left_join(
map_data("state"),
state_count
)
Joining, by = "region"
ggplot(choro, aes(long, lat)) +
geom_polygon(aes(group = group, fill = n)) +
geom_text(data=snames, aes(long, lat, label=n)) +
coord_quickmap()

NA
NA
The range of breweries per state ranges from 1 to 47, with Colorado holding the highest number of breweries per state. The heat map above shows North and South Dakotas along with West Virginia as having the lowest number of breweries (1 per state). Following Colorado is California with 39 and Oregon with 29 breweries. There appears to be higher number of breweries in the north east and west coast, as opposed to the central states, with the exception of Colorado and Texas.
Cleaning the data
Prepering/transforming the data into a usable form for analysis, visualization, etc… #### Merging Dataframes Merge beer data with the breweries data. Print the first 6 observations and the last six observations to check the merged file.
attach(beer)
beer[order(Brewery_id),] # sort the data to determine column for merge
# merge on Brewery ID
breweries_named <- plyr::rename(breweries, c("Brew_ID"="Brewery_id"))
brewing_beer <- merge(breweries_named,beer,by="Brewery_id", all=TRUE) # outter join
brewed_beer <- plyr::rename(brewing_beer, c("Name.x"="Brewery", "Name.y"="Beer")) # rename breweries and beer
head(brewed_beer,6) # show the first 6 rows of data
NA
Missing Data
Missing data are in columns ABV (62) and IBU (1005) only. Cleaning data in multiple options: 1. complete records only 2. replacing NA with the averages of the remainder of the column
colSums(is.na(averaged_beer))
Brewery_id Brewery City State Beer Beer_ID ABV IBU Style Ounces ABVpercent
0 0 0 0 0 0 0 0 0 0 0
Median Alcohol Content
Median of Alcohol by Volume and Bitterness by State
# group by state, get median of ABV, IBU
medians <- as.data.frame(aggregate(completed_beer[,c(7,8)], by=list(completed_beer$State), FUN=median))
median_df <- plyr::rename(medians, c("Group.1"="State")) # rename the column to State
median_graph <- median_df %>% ggplot(aes(x = ABV, y = IBU, color=State)) + geom_point() + ggtitle("Median Alcohol Content and Bitterness by State") # plot scatter plot
ABV_bar <-ggplot(data=median_df, aes(x = State, y = ABV, fill = State)) +
geom_bar(stat="identity", width = 0.75) + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + ggtitle("Median ABV by State")
ggplotly(ABV_bar)
IBU_bar <-ggplot(data=median_df, aes(x = State, y = IBU, fill = State)) +
geom_bar(stat="identity", width = 0.75) + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + ggtitle("Median IBU by State")
ggplotly(IBU_bar)
NA
Max ABV and IBU
Which state has the maximum alcoholic (ABV) beer? Which state has the most bitter (IBU) beer?
# find the max ABV and IBU from each state
maximums <- as.data.frame(aggregate(completed_beer[,c(7,8)], by=list(completed_beer$State), FUN=max))
max_df <- plyr::rename(maximums, c("Group.1"="State")) # rename the column to State
#find the max ABV and IBU states
max_ABV <- max_df %>% filter(ABV == max(ABV))
max_IBU <- max_df %>% filter(IBU == max(IBU))
max_state <- rbind(max_ABV, max_IBU)
max_state
NA
Summary Statistics
The summary statistics and distribution of the ABV variable.
#Creates column representing ABV in percentage which is more user-readable and in-line with how
#ABV is represented by the industry
print("Values below are in percentage (%)")
[1] "Values below are in percentage (%)"
#Using method 1 for handling missing data
completed_beer$ABVpercent <- completed_beer$ABV*100
summary(completed_beer$ABVpercent) #Range, quartiles, and mean of ABV percentage value
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.700 5.000 5.700 5.991 6.800 12.500
cat("Standard deviation: ", sd(completed_beer$ABVpercent)) #Standard deviation of ABV percentage value
Standard deviation: 1.357633
#Using method 2 for handling missing data
averaged_beer$ABVpercent <- averaged_beer$ABV*100
summary(averaged_beer$ABVpercent)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.100 5.000 5.700 5.977 6.700 12.800
cat("Standard deviation: ", sd(averaged_beer$ABVpercent))
Standard deviation: 1.336634
Relationship between Bitterness and Alcohol Content
Is there an apparent relationship between the bitterness of the beer and its alcoholic content? Draw a scatter plot. Make your best judgment of a relationship and EXPLAIN your answer.
#Uses method 1 for handling missing data (remove incomplete lines)
#Correlation between ABV percentage and IBU; function uses Pearson method as default
cor(x=completed_beer$ABVpercent, y=completed_beer$IBU)
[1] 0.6706215
#Visualizations between ABV percentage and IBU
ggplot(data=completed_beer, mapping=aes(x= IBU, y= ABVpercent, position_jitter())) + geom_point()

ggplot(data=completed_beer, mapping=aes(x= ABVpercent, y= IBU, position_jitter())) + geom_point()

#Uses method 2 for handling missing data (replace NAs with average of present data)
#Correlation between ABV percentage and IBU; function uses Pearson method as default
cor(x=averaged_beer$ABVpercent, y=averaged_beer$IBU)
[1] 0.520011
#Visualizations between ABV percentage and IBU
ggplot(data=averaged_beer, mapping=aes(x= IBU, y= ABVpercent, position_jitter())) + geom_point()

ggplot(data=averaged_beer, mapping=aes(x= ABVpercent, y= IBU, position_jitter())) + geom_point()

IPAs vs. Ales
Budweiser would also like to investigate the difference with respect to IBU and ABV between IPAs (India Pale Ales) and other types of Ale (any beer with “Ale” in its name other than IPA). You decide to use KNN classification to investigate this relationship. Provide statistical evidence one way or the other. You can of course assume your audience is comfortable with percentages … KNN is very easy to understand conceptually.
In addition, while you have decided to use KNN to investigate this relationship (KNN is required) you may also feel free to supplement your response to this question with any other methods or techniques you have learned. Creativity and alternative solutions are always encouraged.
ipa = brewed_beer[grep("IPA", brewed_beer$Style), ]
not_ipa = brewed_beer[-grep("IPA", brewed_beer$Style), ]
ales = not_ipa[grep("Ale", not_ipa$Style), ]
Additional inferences
Knock their socks off! Find one other useful inference from the data that you feel Budweiser may be able to find value in. You must convince them why it is important and back up your conviction with appropriate statistical evidence.
LS0tCnRpdGxlOiAiQnJld2luZyBVcCBhIFN0b3JtIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQojIyMgSW50cm9kdWN0aW9uCgpgYGB7cn0KIyBsb2FkIGluIHRoZSBsaWJyYXJpZXMKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyBBbmFseXNpcwoKIyMjIFJlYWQgaW4gdGhlIGRhdGEKUmVhZCBpbiBkYXRhIGZyb20gZmlsZXMKYGBge3J9CmJlZXIgPSByZWFkLmNzdigiLi9kYXRhL0JlZXJzLmNzdiIsaGVhZGVyID0gVFJVRSkKYnJld2VyaWVzID0gcmVhZC5jc3YoIi4vZGF0YS9CcmV3ZXJpZXMuY3N2IixoZWFkZXIgPSBUUlVFLCBzdHJpcC53aGl0ZSA9IFRSVUUpCgojZGlzcGxheSB0aGUgZGF0YWZyYW1lcwpiZWVyCmJyZXdlcmllcwoKYGBgCgoKIyMjIEhvdyBtYW55IGJyZXdlcmllcyBhcmUgdGhlcmUgaW4gZWFjaCBzdGF0ZT8KCmBgYHtyfQoKc3RyKGJyZXdlcmllcykgIyBjaGVjayB0aGF0IFN0YXRlIGlzIGEgRmFjdG9yCgpzdGF0ZV9icmV3ZWllcyA8LSBicmV3ZXJpZXMgJT4lIGdyb3VwX2J5KFN0YXRlKSAlPiUgdGFsbHkoKSAjIGNvdW50IHRoZSBudW1iZXIgb2YgYnJld2VyaWVzIHdpdGhpbiBhIHN0YXRlCnVuaXF1ZV9zdGF0ZV9icmV3ZWllcyA8LSBicmV3ZXJpZXMgJT4lIGdyb3VwX2J5KFN0YXRlKSAlPiUgdGFsbHkobl9kaXN0aW5jdChOYW1lKSkgIyBjaGVjayBmb3IgYW55IGR1cGxpY2F0ZXMKCmBgYAoKIyMjIyBIZWF0IE1hcCBvZiBCcmV3ZWllcyBwZXIgU3RhdGUKCmBgYHtyfQojIyBhZGQgbG93ZXJjYXNlIHN0YXRlIG5hbWUgZm9yIGhlYXQgbWFwCmxzdGF0ZXMgPSB0b2xvd2VyKHN0YXRlLm5hbWUpCnN0YXRlX2NvdW50ID0gc3RhdGVfYnJld2VpZXMgJT4lIAogICAgYWRkX3Jvd25hbWVzKCJyZWdpb24iKSAlPiUgCiAgICBtdXRhdGUoIHJlZ2lvbj1sc3RhdGVzW21hdGNoKFN0YXRlLCBzdGF0ZS5hYmIpXSApCgojIyBmaW5kIGNlbnRlciBvZiBlYWNoIHN0YXRlIGZvciB0ZXh0IHBvc2l0aW9uIApzbmFtZXMgPC0gZGF0YS5mcmFtZShyZWdpb249bHN0YXRlcywgbG9uZz1zdGF0ZS5jZW50ZXIkeCwgbGF0PXN0YXRlLmNlbnRlciR5KQpzbmFtZXMgPC0gbWVyZ2Uoc25hbWVzLCBzdGF0ZV9jb3VudCwgYnk9InJlZ2lvbiIpCgojIyBtZXJnZSBtYXAgZGF0YSB3aXRoIGNvdW50cyBkYXRhCmNob3JvIDwtIGxlZnRfam9pbigKICBtYXBfZGF0YSgic3RhdGUiKSwgCiAgc3RhdGVfY291bnQKKQpnZ3Bsb3QoY2hvcm8sIGFlcyhsb25nLCBsYXQpKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyhncm91cCA9IGdyb3VwLCBmaWxsID0gbikpICsgCiAgZ2VvbV90ZXh0KGRhdGE9c25hbWVzLCBhZXMobG9uZywgbGF0LCBsYWJlbD1uKSkgKwogIGNvb3JkX3F1aWNrbWFwKCkKCgpgYGAKVGhlIHJhbmdlIG9mIGJyZXdlcmllcyBwZXIgc3RhdGUgcmFuZ2VzIGZyb20gMSB0byA0Nywgd2l0aCBDb2xvcmFkbyBob2xkaW5nIHRoZSBoaWdoZXN0IG51bWJlciBvZiBicmV3ZXJpZXMgcGVyIHN0YXRlLiBUaGUgaGVhdCBtYXAgYWJvdmUgc2hvd3MgTm9ydGggYW5kIFNvdXRoIERha290YXMgYWxvbmcgd2l0aCBXZXN0IFZpcmdpbmlhIGFzIGhhdmluZyB0aGUgbG93ZXN0IG51bWJlciBvZiBicmV3ZXJpZXMgKDEgcGVyIHN0YXRlKS4gRm9sbG93aW5nIENvbG9yYWRvIGlzIENhbGlmb3JuaWEgd2l0aCAzOSBhbmQgT3JlZ29uIHdpdGggMjkgYnJld2VyaWVzLiBUaGVyZSBhcHBlYXJzIHRvIGJlIGhpZ2hlciBudW1iZXIgb2YgYnJld2VyaWVzIGluIHRoZSBub3J0aCBlYXN0IGFuZCB3ZXN0IGNvYXN0LCBhcyBvcHBvc2VkIHRvIHRoZSBjZW50cmFsIHN0YXRlcywgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIENvbG9yYWRvIGFuZCBUZXhhcy4gCgojIyMgQ2xlYW5pbmcgdGhlIGRhdGEKUHJlcGVyaW5nL3RyYW5zZm9ybWluZyB0aGUgZGF0YSBpbnRvIGEgdXNhYmxlIGZvcm0gZm9yIGFuYWx5c2lzLCB2aXN1YWxpemF0aW9uLCBldGMuLi4gCiMjIyMgTWVyZ2luZyBEYXRhZnJhbWVzCk1lcmdlIGJlZXIgZGF0YSB3aXRoIHRoZSBicmV3ZXJpZXMgZGF0YS4gUHJpbnQgdGhlIGZpcnN0IDYgb2JzZXJ2YXRpb25zIGFuZCB0aGUgbGFzdCBzaXggb2JzZXJ2YXRpb25zIHRvIGNoZWNrIHRoZSBtZXJnZWQgZmlsZS4gIApgYGB7cn0KCmF0dGFjaChiZWVyKQpiZWVyW29yZGVyKEJyZXdlcnlfaWQpLF0gIyBzb3J0IHRoZSBkYXRhIHRvIGRldGVybWluZSBjb2x1bW4gZm9yIG1lcmdlCgojIG1lcmdlIG9uIEJyZXdlcnkgSUQKYnJld2VyaWVzX25hbWVkIDwtIHBseXI6OnJlbmFtZShicmV3ZXJpZXMsIGMoIkJyZXdfSUQiPSJCcmV3ZXJ5X2lkIikpCgpicmV3aW5nX2JlZXIgPC0gbWVyZ2UoYnJld2VyaWVzX25hbWVkLGJlZXIsYnk9IkJyZXdlcnlfaWQiLCBhbGw9VFJVRSkgIyBvdXR0ZXIgam9pbgoKYnJld2VkX2JlZXIgPC0gcGx5cjo6cmVuYW1lKGJyZXdpbmdfYmVlciwgYygiTmFtZS54Ij0iQnJld2VyeSIsICJOYW1lLnkiPSJCZWVyIikpICMgcmVuYW1lIGJyZXdlcmllcyBhbmQgYmVlcgoKaGVhZChicmV3ZWRfYmVlciw2KSAjIHNob3cgdGhlIGZpcnN0IDYgcm93cyBvZiBkYXRhCgpgYGAKCiMjIyMgTWlzc2luZyBEYXRhCk1pc3NpbmcgZGF0YSBhcmUgaW4gY29sdW1ucyBBQlYgKDYyKSBhbmQgSUJVICgxMDA1KSBvbmx5LiAKQ2xlYW5pbmcgZGF0YSBpbiBtdWx0aXBsZSBvcHRpb25zOgoxLiBjb21wbGV0ZSByZWNvcmRzIG9ubHkKMi4gcmVwbGFjaW5nIE5BIHdpdGggdGhlIGF2ZXJhZ2VzIG9mIHRoZSByZW1haW5kZXIgb2YgdGhlIGNvbHVtbgoKYGBge3J9CiMgc2VsZWN0aW5nIG9ubHkgY29tcGxldGUgY2FzZXMKd2hpY2goaXMubmEoYnJld2VkX2JlZXIpKSAjIGRldGVybWluZSB3aGljaCByb3dzIGNvbnRhaW4gTkEKY29sU3Vtcyhpcy5uYShicmV3ZWRfYmVlcikpICMgc3VtbWFyeSBvZiB0aGUgbnVtYmVyIG9mIE5BIGluIGVhY2ggY29sdW1uCmNvbXBsZXRlZF9iZWVyIDwtIGJyZXdlZF9iZWVyW2NvbXBsZXRlLmNhc2VzKGJyZXdlZF9iZWVyKSwgXSAjIGRmIHdpdGggb25seSBjb21wbGV0ZSByZWNvcmRzCgojIHJlcGxhY2luZyBOQSB3aXRoIGF2ZXJhZ2VzCmF2ZXJhZ2VkX2JlZXIgPC0gYnJld2VkX2JlZXIgIyBtYWtlIGEgZHVwbGljYXRlIG9mIHRoZSBvcmlnaW5hbCBkZiB0byBtYW5pcHVsYXRlCmF2ZXJhZ2VBQlYgPC0gbWVhbihhdmVyYWdlZF9iZWVyJEFCViwgbmEucm0gPSBUUlVFKSAjIHNldCBhdmVyYWdlIHZhcmlhYmxlIGZvciBBQlYKYXZlcmFnZUlCVSA8LSBtZWFuKGF2ZXJhZ2VkX2JlZXIkSUJVLCBuYS5ybSA9IFRSVUUpICMgc2V0IGF2ZXJhZ2UgdmFyaWFibGUgZm9yIElCVQoKI3JlcGxhY2UgTkEgdmFsdWVzIHdpdGggdGhlIGF2ZXJhZ2Ugb2YgdGhlIHByZXNlbnQgdmFsdWVzCmF2ZXJhZ2VkX2JlZXIkSUJVW2lzLm5hKGF2ZXJhZ2VkX2JlZXIkSUJVKV08LWF2ZXJhZ2VJQlUKYXZlcmFnZWRfYmVlciRBQlZbaXMubmEoYXZlcmFnZWRfYmVlciRBQlYpXTwtYXZlcmFnZUFCVgoKCmNvbFN1bXMoaXMubmEoYXZlcmFnZWRfYmVlcikpCgpgYGAKCiMjIyBNZWRpYW4gQWxjb2hvbCBDb250ZW50Ck1lZGlhbiBvZiBBbGNvaG9sIGJ5IFZvbHVtZSBhbmQgQml0dGVybmVzcyBieSBTdGF0ZQpgYGB7cn0KIyBncm91cCBieSBzdGF0ZSwgZ2V0IG1lZGlhbiBvZiBBQlYsIElCVQptZWRpYW5zIDwtIGFzLmRhdGEuZnJhbWUoYWdncmVnYXRlKGNvbXBsZXRlZF9iZWVyWyxjKDcsOCldLCBieT1saXN0KGNvbXBsZXRlZF9iZWVyJFN0YXRlKSwgRlVOPW1lZGlhbikpIAptZWRpYW5fZGYgPC0gcGx5cjo6cmVuYW1lKG1lZGlhbnMsIGMoIkdyb3VwLjEiPSJTdGF0ZSIpKSAjIHJlbmFtZSB0aGUgY29sdW1uIHRvIFN0YXRlCgptZWRpYW5fZ3JhcGggPC0gbWVkaWFuX2RmICU+JSBnZ3Bsb3QoYWVzKHggPSBBQlYsIHkgPSBJQlUsIGNvbG9yPVN0YXRlKSkgKyBnZW9tX3BvaW50KCkgKyBnZ3RpdGxlKCJNZWRpYW4gQWxjb2hvbCBDb250ZW50IGFuZCBCaXR0ZXJuZXNzIGJ5IFN0YXRlIikgIyBwbG90IHNjYXR0ZXIgcGxvdAoKQUJWX2JhciA8LWdncGxvdChkYXRhPW1lZGlhbl9kZiwgYWVzKHggPSBTdGF0ZSwgeSA9IEFCViwgZmlsbCA9IFN0YXRlKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGggPSAwLjc1KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgZ2d0aXRsZSgiTWVkaWFuIEFCViBieSBTdGF0ZSIpCmdncGxvdGx5KEFCVl9iYXIpCgpJQlVfYmFyIDwtZ2dwbG90KGRhdGE9bWVkaWFuX2RmLCBhZXMoeCA9IFN0YXRlLCB5ID0gSUJVLCBmaWxsID0gU3RhdGUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aCA9IDAuNzUpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKyBnZ3RpdGxlKCJNZWRpYW4gSUJVIGJ5IFN0YXRlIikKZ2dwbG90bHkoSUJVX2JhcikKCmBgYAojIyMgTWF4IEFCViBhbmQgSUJVCldoaWNoIHN0YXRlIGhhcyB0aGUgbWF4aW11bSBhbGNvaG9saWMgKEFCVikgYmVlcj8gV2hpY2ggc3RhdGUgaGFzIHRoZSBtb3N0IGJpdHRlciAoSUJVKSBiZWVyPwpgYGB7cn0KIyBmaW5kIHRoZSBtYXggQUJWIGFuZCBJQlUgZnJvbSBlYWNoIHN0YXRlCm1heGltdW1zIDwtIGFzLmRhdGEuZnJhbWUoYWdncmVnYXRlKGNvbXBsZXRlZF9iZWVyWyxjKDcsOCldLCBieT1saXN0KGNvbXBsZXRlZF9iZWVyJFN0YXRlKSwgRlVOPW1heCkpCm1heF9kZiA8LSBwbHlyOjpyZW5hbWUobWF4aW11bXMsIGMoIkdyb3VwLjEiPSJTdGF0ZSIpKSAjIHJlbmFtZSB0aGUgY29sdW1uIHRvIFN0YXRlCgojZmluZCB0aGUgbWF4IEFCViBhbmQgSUJVIHN0YXRlcwptYXhfQUJWIDwtIG1heF9kZiAlPiUgZmlsdGVyKEFCViA9PSBtYXgoQUJWKSkKbWF4X0lCVSA8LSBtYXhfZGYgJT4lIGZpbHRlcihJQlUgPT0gbWF4KElCVSkpCgptYXhfc3RhdGUgPC0gcmJpbmQobWF4X0FCViwgbWF4X0lCVSkKbWF4X3N0YXRlCgpgYGAKCiMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MKVGhlIHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgZGlzdHJpYnV0aW9uIG9mIHRoZSBBQlYgdmFyaWFibGUuCgpgYGB7cn0KI0NyZWF0ZXMgY29sdW1uIHJlcHJlc2VudGluZyBBQlYgaW4gcGVyY2VudGFnZSB3aGljaCBpcyBtb3JlIHVzZXItcmVhZGFibGUgYW5kIGluLWxpbmUgd2l0aCBob3cKI0FCViBpcyByZXByZXNlbnRlZCBieSB0aGUgaW5kdXN0cnkKcHJpbnQoIlZhbHVlcyBiZWxvdyBhcmUgaW4gcGVyY2VudGFnZSAoJSkiKQoKI1VzaW5nIG1ldGhvZCAxIGZvciBoYW5kbGluZyBtaXNzaW5nIGRhdGEKY29tcGxldGVkX2JlZXIkQUJWcGVyY2VudCA8LSBjb21wbGV0ZWRfYmVlciRBQlYqMTAwIAoKc3VtbWFyeShjb21wbGV0ZWRfYmVlciRBQlZwZXJjZW50KSAjUmFuZ2UsIHF1YXJ0aWxlcywgYW5kIG1lYW4gb2YgQUJWIHBlcmNlbnRhZ2UgdmFsdWUKCmNhdCgiU3RhbmRhcmQgZGV2aWF0aW9uOiAiLCBzZChjb21wbGV0ZWRfYmVlciRBQlZwZXJjZW50KSkgI1N0YW5kYXJkIGRldmlhdGlvbiBvZiBBQlYgcGVyY2VudGFnZSB2YWx1ZQoKI1VzaW5nIG1ldGhvZCAyIGZvciBoYW5kbGluZyBtaXNzaW5nIGRhdGEKYXZlcmFnZWRfYmVlciRBQlZwZXJjZW50IDwtIGF2ZXJhZ2VkX2JlZXIkQUJWKjEwMAoKc3VtbWFyeShhdmVyYWdlZF9iZWVyJEFCVnBlcmNlbnQpCgpjYXQoIlN0YW5kYXJkIGRldmlhdGlvbjogIiwgc2QoYXZlcmFnZWRfYmVlciRBQlZwZXJjZW50KSkKCgpgYGAKCiMjIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBCaXR0ZXJuZXNzIGFuZCBBbGNvaG9sIENvbnRlbnQKSXMgdGhlcmUgYW4gYXBwYXJlbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGJpdHRlcm5lc3Mgb2YgdGhlIGJlZXIgYW5kIGl0cyBhbGNvaG9saWMgY29udGVudD8gRHJhdyBhIHNjYXR0ZXIgcGxvdC4gIE1ha2UgeW91ciBiZXN0IGp1ZGdtZW50IG9mIGEgcmVsYXRpb25zaGlwIGFuZCBFWFBMQUlOIHlvdXIgYW5zd2VyLgpgYGB7cn0KI1VzZXMgbWV0aG9kIDEgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSAocmVtb3ZlIGluY29tcGxldGUgbGluZXMpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVOyBmdW5jdGlvbiB1c2VzIFBlYXJzb24gbWV0aG9kIGFzIGRlZmF1bHQKY29yKHg9Y29tcGxldGVkX2JlZXIkQUJWcGVyY2VudCwgeT1jb21wbGV0ZWRfYmVlciRJQlUpCgojVmlzdWFsaXphdGlvbnMgYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVCmdncGxvdChkYXRhPWNvbXBsZXRlZF9iZWVyLCBtYXBwaW5nPWFlcyh4PSBJQlUsIHk9IEFCVnBlcmNlbnQsIHBvc2l0aW9uX2ppdHRlcigpKSkgKyBnZW9tX3BvaW50KCkKCmdncGxvdChkYXRhPWNvbXBsZXRlZF9iZWVyLCBtYXBwaW5nPWFlcyh4PSBBQlZwZXJjZW50LCB5PSBJQlUsIHBvc2l0aW9uX2ppdHRlcigpKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KI1VzZXMgbWV0aG9kIDIgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSAocmVwbGFjZSBOQXMgd2l0aCBhdmVyYWdlIG9mIHByZXNlbnQgZGF0YSkKCiNDb3JyZWxhdGlvbiBiZXR3ZWVuIEFCViBwZXJjZW50YWdlIGFuZCBJQlU7IGZ1bmN0aW9uIHVzZXMgUGVhcnNvbiBtZXRob2QgYXMgZGVmYXVsdApjb3IoeD1hdmVyYWdlZF9iZWVyJEFCVnBlcmNlbnQsIHk9YXZlcmFnZWRfYmVlciRJQlUpCgojVmlzdWFsaXphdGlvbnMgYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVCmdncGxvdChkYXRhPWF2ZXJhZ2VkX2JlZXIsIG1hcHBpbmc9YWVzKHg9IElCVSwgeT0gQUJWcGVyY2VudCwgcG9zaXRpb25faml0dGVyKCkpKSArIGdlb21fcG9pbnQoKQoKZ2dwbG90KGRhdGE9YXZlcmFnZWRfYmVlciwgbWFwcGluZz1hZXMoeD0gQUJWcGVyY2VudCwgeT0gSUJVLCBwb3NpdGlvbl9qaXR0ZXIoKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKIyMjIElQQXMgdnMuIEFsZXMKQnVkd2Vpc2VyIHdvdWxkIGFsc28gbGlrZSB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW5jZSB3aXRoIHJlc3BlY3QgdG8gSUJVIGFuZCBBQlYgYmV0d2VlbiBJUEFzIChJbmRpYSBQYWxlIEFsZXMpIGFuZCBvdGhlciB0eXBlcyBvZiBBbGUgKGFueSBiZWVyIHdpdGgg4oCcQWxl4oCdIGluIGl0cyBuYW1lIG90aGVyIHRoYW4gSVBBKS4gIFlvdSBkZWNpZGUgdG8gdXNlIEtOTiBjbGFzc2lmaWNhdGlvbiB0byBpbnZlc3RpZ2F0ZSB0aGlzIHJlbGF0aW9uc2hpcC4gIFByb3ZpZGUgc3RhdGlzdGljYWwgZXZpZGVuY2Ugb25lIHdheSBvciB0aGUgb3RoZXIuIFlvdSBjYW4gb2YgY291cnNlIGFzc3VtZSB5b3VyIGF1ZGllbmNlIGlzIGNvbWZvcnRhYmxlIHdpdGggcGVyY2VudGFnZXMg4oCmIEtOTiBpcyB2ZXJ5IGVhc3kgdG8gdW5kZXJzdGFuZCBjb25jZXB0dWFsbHkuCgpJbiBhZGRpdGlvbiwgd2hpbGUgeW91IGhhdmUgZGVjaWRlZCB0byB1c2UgS05OIHRvIGludmVzdGlnYXRlIHRoaXMgcmVsYXRpb25zaGlwIChLTk4gaXMgcmVxdWlyZWQpIHlvdSBtYXkgYWxzbyBmZWVsIGZyZWUgdG8gc3VwcGxlbWVudCB5b3VyIHJlc3BvbnNlIHRvIHRoaXMgcXVlc3Rpb24gd2l0aCBhbnkgb3RoZXIgbWV0aG9kcyBvciB0ZWNobmlxdWVzIHlvdSBoYXZlIGxlYXJuZWQuICBDcmVhdGl2aXR5IGFuZCBhbHRlcm5hdGl2ZSBzb2x1dGlvbnMgYXJlIGFsd2F5cyBlbmNvdXJhZ2VkLiAgCmBgYHtyfQppcGEgPSBicmV3ZWRfYmVlcltncmVwKCJJUEEiLCBicmV3ZWRfYmVlciRTdHlsZSksIF0Kbm90X2lwYSA9IGJyZXdlZF9iZWVyWy1ncmVwKCJJUEEiLCBicmV3ZWRfYmVlciRTdHlsZSksIF0KYWxlcyA9IG5vdF9pcGFbZ3JlcCgiQWxlIiwgbm90X2lwYSRTdHlsZSksIF0KCmBgYAoKIyMjIEFkZGl0aW9uYWwgaW5mZXJlbmNlcwpLbm9jayB0aGVpciBzb2NrcyBvZmYhICBGaW5kIG9uZSBvdGhlciB1c2VmdWwgaW5mZXJlbmNlIGZyb20gdGhlIGRhdGEgdGhhdCB5b3UgZmVlbCBCdWR3ZWlzZXIgbWF5IGJlIGFibGUgdG8gZmluZCB2YWx1ZSBpbi4gIFlvdSBtdXN0IGNvbnZpbmNlIHRoZW0gd2h5IGl0IGlzIGltcG9ydGFudCBhbmQgYmFjayB1cCB5b3VyIGNvbnZpY3Rpb24gd2l0aCBhcHByb3ByaWF0ZSBzdGF0aXN0aWNhbCBldmlkZW5jZS4gCmBgYHtyfQoKYGBgCgoKCgoKCgoKCgo=